#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "imbus_internal.h"
#include "im_array.h"
#include "im_text.h"
#include "im_string.h"
#include "im_lookup_table.h"

/************************** Declaration of internal functions ***************/
static void     __im_candidate_initialize       (IMPointer       candidate);
static void     __im_candidate_finalize         (IMPointer       candidate);
static void     __im_candidate_copy             (IMPointer       dest,
                                                 IMConstPointer  src);
static IMBool   __im_candidate_serialize        (IMConstPointer  obj,
                                                 IMPointer       stream);
static IMBool   __im_candidate_deserialize      (IMPointer       obj,
                                                 IMPointer       stream);
static void     __im_candidate_class_init       (IMPointer       klass);

static void     __im_lookup_table_initialize    (IMPointer       table);
static void     __im_lookup_table_finalize      (IMPointer       table);
static void     __im_lookup_table_copy          (IMPointer       dest,
                                                 IMConstPointer  src);
static IMBool   __im_lookup_table_serialize     (IMConstPointer  obj,
                                                 IMPointer       stream);
static IMBool   __im_lookup_table_deserialize   (IMPointer       obj,
                                                 IMPointer       stream);
static void     __im_lookup_table_class_init    (IMPointer       klass);

/****************** Declaration of object and class structure ************/
struct _IMCandidate
{
    IMObject object;

    IMText      *content;
    IMText      *label;
    IMString    *icon;
    IMPointer    annotation;
};

struct _IMCandidateClass
{
    IMObjectClass parent_class;
};

struct _IMLookupTable
{
    IMObject object;

    /* private */
    IMUInt32     id;
    IMUInt32     cols;
    IMUInt32     rows;
    IMUInt32     cursor;
    IMUInt32     num_pages;
    IMUInt32     cur_page;
    IMUInt16     orientation;
    IMUInt16     flags;

    IMText      *title;

    IMPtrArray  *candidates;
};

struct _IMLookupTableClass
{
    IMObjectClass parent_class;
};

/************************ Internal varable ************************/
static const IMObjectClass *__im_candidate_parent_class    = 0;
static const IMObjectClass *__im_lookup_table_parent_class = 0;

/************* Implementation of internal functions ***************/
static void
__im_candidate_initialize (IMPointer candidate)
{
    ((IMCandidate*)candidate)->content     = 0;
    ((IMCandidate*)candidate)->label       = 0;
    ((IMCandidate*)candidate)->icon        = 0;
    ((IMCandidate*)candidate)->annotation  = 0;
}

static void
__im_candidate_finalize (IMPointer candidate)
{
    IMCandidate *cp = IM_CANDIDATE (candidate);

    _im_assert (cp != 0);

    if (cp->content)
        im_object_unref (cp->content);
    if (cp->label)
        im_object_unref (cp->label);
    if (cp->icon)
        im_object_unref (cp->icon);
    if (cp->annotation)
        im_object_unref (cp->annotation);
}

static void
__im_candidate_copy (IMPointer dest, IMConstPointer src)
{
    IMCandidate         *dcp = IM_CANDIDATE (dest);
    const IMCandidate   *scp = IM_CONST_CANDIDATE (src);

    _im_assert (dcp && scp);

    __im_candidate_finalize (dcp);

    if (scp->content)
        dcp->content    = im_object_ref (im_object_clone (scp->content));
    else
        dcp->content    = 0;

    if (scp->label)
        dcp->label      = im_object_ref (im_object_clone (scp->label));
    else
        dcp->label      = 0;

    if (scp->icon)
        dcp->icon       = im_object_ref (im_object_clone (scp->icon));
    else
        dcp->icon       = 0;

    if (scp->annotation)
        dcp->annotation = im_object_ref (im_object_clone (scp->annotation));
    else
        dcp->annotation = 0;
}

static IMBool
__im_candidate_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMCandidate *cp = IM_CONST_CANDIDATE (obj);
    IMStream          *sp = IM_STREAM (stream);
    IMBool             ret;

    _im_assert (cp && sp);

    /* Call parent serialize method */
    if (!__im_candidate_parent_class->serialize (obj, stream))
        return FALSE;

    if (cp->content)
        ret = im_stream_put_object (sp, cp->content);
    else
        ret = im_stream_put_void (sp);
    if (!ret) return FALSE;
    if (cp->label)
        ret = im_stream_put_object (sp, cp->label);
    else
        ret = im_stream_put_void (sp);
    if (!ret) return FALSE;
    if (cp->icon)
        ret = im_stream_put_object (sp, cp->icon);
    else
        ret = im_stream_put_void (sp);
    if (!ret) return FALSE;
    if (cp->annotation)
        ret = im_stream_put_object (sp, cp->annotation);
    else
        ret = im_stream_put_void (sp);
    return ret;
}

static IMBool
__im_candidate_deserialize (IMPointer obj, IMPointer stream)
{
    IMCandidate *cp = IM_CANDIDATE (obj);
    IMStream    *sp = IM_STREAM (stream);
    IMType       type;

    _im_assert (cp && sp);

    /* Call parent deserialize method */
    if (!__im_candidate_parent_class->deserialize (obj, stream))
        return FALSE;

    __im_candidate_finalize (cp);

    /* content */
    cp->content = 0;
    if (im_stream_get_data_type (sp) == IM_TYPE_TEXT) {
        cp->content = im_object_ref (im_text_new ());
        if (!im_stream_get_object (sp, cp->content)) {
            im_object_unref (cp->content);
            cp->content = 0;
            return FALSE;
        }
    } else if (!im_stream_get_void (sp)) {
        return FALSE;
    }

    /* label */
    cp->label = 0;
    if (im_stream_get_data_type (sp) == IM_TYPE_TEXT) {
        cp->label = im_object_ref (im_text_new ());
        if (!im_stream_get_object (sp, cp->label)) {
            im_object_unref (cp->label);
            cp->label = 0;
            return FALSE;
        }
    } else if (!im_stream_get_void (sp)) {
        return FALSE;
    }

    /* icon */
    cp->icon = 0;
    if (im_stream_get_data_type (sp) == IM_TYPE_STRING) {
        cp->icon = im_object_ref (im_string_new (0));
        if (!im_stream_get_object (sp, cp->icon)) {
            im_object_unref (cp->icon);
            cp->icon = 0;
            return FALSE;
        }
    } else if (!im_stream_get_void (sp)) {
        return FALSE;
    }

    /* annotation */
    cp->annotation = 0;
    type = im_stream_get_data_type (sp);
    if (IM_TYPE_IS_OBJECT (type)) {
        cp->annotation = im_object_ref (im_object_new (type));
        if (!im_stream_get_object (sp, cp->annotation)) {
            im_object_unref (cp->annotation);
            cp->annotation = 0;
            return FALSE;
        }
    } else if (!im_stream_get_void (sp)) {
        return FALSE;
    }

    return TRUE;
}

static void
__im_candidate_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *)klass;

    cp->copy        = __im_candidate_copy;
    cp->serialize   = __im_candidate_serialize;
    cp->deserialize = __im_candidate_deserialize;

    __im_candidate_parent_class = im_object_class_get_parent (cp);
}

static void
__im_lookup_table_initialize (IMPointer table)
{
    ((IMLookupTable*)table)->id            = 0;
    ((IMLookupTable*)table)->cols          = 10;
    ((IMLookupTable*)table)->rows          = 1;
    ((IMLookupTable*)table)->cursor        = 0;
    ((IMLookupTable*)table)->num_pages     = 1;
    ((IMLookupTable*)table)->cur_page      = 0;
    ((IMLookupTable*)table)->title         = 0;
    ((IMLookupTable*)table)->orientation   = IM_LOOKUP_TABLE_LTRTTB;
    ((IMLookupTable*)table)->flags         = 0;
    ((IMLookupTable*)table)->candidates    = im_ptr_array_new ();
}

static void
__free_object (IMPointer obj, IMPointer user_data)
{
    if (obj)
        im_object_unref (obj);
}

static void
__im_lookup_table_finalize (IMPointer table)
{
    IMLookupTable *tp = IM_LOOKUP_TABLE (table);

    _im_assert (tp != 0);

    if (tp->title)
        im_object_unref (tp->title);

    im_ptr_array_foreach (tp->candidates, __free_object, 0);
    im_ptr_array_destroy (tp->candidates, TRUE);
}

static void
__im_lookup_table_copy (IMPointer dest, IMConstPointer src)
{
    IMLookupTable       *dtp = IM_LOOKUP_TABLE (dest);
    const IMLookupTable *stp = IM_CONST_LOOKUP_TABLE (src);
    IMSize i;

    _im_assert (dtp && stp);

    im_lookup_table_clear (dest);

    dtp->id            = stp->id;
    dtp->cols          = stp->cols;
    dtp->rows          = stp->rows;
    dtp->cursor        = stp->cursor;
    dtp->num_pages     = stp->num_pages;
    dtp->cur_page      = stp->cur_page;
    dtp->flags         = stp->flags;
    dtp->orientation   = stp->orientation;

    /* copy title */
    if (stp->title)
        dtp->title = im_object_ref (im_object_clone (stp->title));
    else
        dtp->title = 0;

    /* copy candidates */
    im_ptr_array_set_size (dtp->candidates, im_ptr_array_size(stp->candidates));
    for (i = 0; i < im_ptr_array_size(stp->candidates); ++i)
        if (im_ptr_array_index(stp->candidates,i))
            im_ptr_array_index(dtp->candidates,i) = im_object_ref (im_object_clone (im_ptr_array_index(stp->candidates,i)));
        else
            im_ptr_array_index(dtp->candidates,i) = 0;

    /* Call copy method of parent class */
    __im_lookup_table_parent_class->copy (dest, src);
}

static IMBool
__im_lookup_table_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMLookupTable *tp = IM_CONST_LOOKUP_TABLE (obj);
    IMStream            *sp = IM_STREAM (stream);

    IMUInt32  buf [8];
    IMSize    i;
    IMSize    size;
    IMBool    ret;

    _im_assert (tp && sp);

    /* Call parent serialize method */
    if (!__im_lookup_table_parent_class->serialize (obj, stream))
        return FALSE;

    size = tp->cols * tp->rows;

    buf[0] = tp->id;
    buf[1] = tp->cols;
    buf[2] = tp->rows;
    buf[3] = tp->cursor;
    buf[4] = tp->num_pages;
    buf[5] = tp->cur_page;
    buf[6] = ((IMUInt32)tp->flags) | (((IMUInt32)tp->orientation) << 16);
    buf[7] = IM_MIN (im_ptr_array_size (tp->candidates), size);

    if (!im_stream_put_uint32_array (sp, buf, 8))
        return FALSE;

    /* save title */
    if (tp->title)
        ret = im_stream_put_object (sp, tp->title);
    else
        ret = im_stream_put_void (sp);
    if (!ret) return FALSE;

    /* save candidates */
    for (i = 0; i < buf[7]; ++i) {
        if (im_ptr_array_index(tp->candidates,i))
            ret = im_stream_put_object (sp, im_ptr_array_index(tp->candidates,i));
        else
            ret = im_stream_put_void (sp);
        if (!ret) return FALSE;
    }
    return TRUE;
}

static IMBool
__im_lookup_table_deserialize (IMPointer obj, IMPointer stream)
{
    IMLookupTable *tp = IM_LOOKUP_TABLE (obj);
    IMStream      *sp = IM_STREAM (stream);

    IMUInt32 buf [8];
    IMSize   i;
    IMType   type;

    _im_assert (tp && sp);

    /* Call parent deserialize method */
    if (!__im_lookup_table_parent_class->deserialize (obj, stream))
        return FALSE;

    if (im_stream_get_data_type (sp) != IM_TYPE_UINT32_ARRAY || im_stream_get_data_size (sp) != 8)
        return FALSE;

    if (!im_stream_get_uint32_array (sp, buf, 8))
        return FALSE;

    /* Check whether page size is valid */
    if (!buf[1] || !buf[2])
        return FALSE;

    /* Check whether following data is a IMText or a VOID.
     * It's the title*/
    type = im_stream_get_data_type (sp);
    if (type != IM_TYPE_TEXT && type != IM_TYPE_VOID)
        return FALSE;

    im_lookup_table_clear (tp);

    tp->id          = buf[0];
    tp->cols        = buf[1];
    tp->rows        = buf[2];
    tp->cursor      = buf[3];
    tp->num_pages   = buf[4];
    tp->cur_page    = buf[5];
    tp->flags       = buf[6] & 0xffff;
    tp->orientation = (buf[6] >> 16);

    /* load title */
    if (type == IM_TYPE_TEXT) {
        /* Newly created IMText object should be owned by the IMLookupTable object.*/
        tp->title = im_object_ref (im_text_new ());
        if (!im_stream_get_object (sp, tp->title)) {
            im_object_unref (tp->title);
            tp->title = 0;
            return FALSE;
        }
    } else if (!im_stream_get_void (sp)) {
        return FALSE;
    }

    /* load candidates */
    im_ptr_array_set_size (tp->candidates, buf[7]);
    for (i = 0; i < buf[7]; ++i) {
        type = im_stream_get_data_type (sp);
        if (type == IM_TYPE_CANDIDATE) {
            /* Newly created IMCandidate object should be owned by the IMLookupTable object.*/
            im_ptr_array_index(tp->candidates,i) = im_object_ref (im_object_new (type));
            if (!im_stream_get_object (sp, im_ptr_array_index(tp->candidates,i))) {
                im_object_unref (im_ptr_array_index(tp->candidates,i));
                im_ptr_array_index(tp->candidates,i) = 0;
                return FALSE;
            }
        } else if (type != IM_TYPE_VOID || !im_stream_get_void (sp)) {
            return FALSE;
        }
    }

    return TRUE;
}

static void
__im_lookup_table_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *)klass;

    cp->copy        = __im_lookup_table_copy;
    cp->serialize   = __im_lookup_table_serialize;
    cp->deserialize = __im_lookup_table_deserialize;

    __im_lookup_table_parent_class = im_object_class_get_parent (cp);
}

/************* Implementation of public functions ***************/
void
__im_type_register_lookup_table ()
{
    static IMTypeInfo im_candidate_type_info =
    {
        sizeof (IMCandidateClass),      /**< class_size >**/
        __im_candidate_class_init,      /**< class_init >**/
        0,                              /**< class_finalize >**/

        sizeof (IMCandidate),           /**< instance_size >**/
        __im_candidate_initialize,      /**< instance_init >**/
        __im_candidate_finalize,        /**< instance_finalize >**/
    };

    static IMTypeInfo im_lookup_table_type_info =
    {
        sizeof (IMLookupTableClass),    /**< class_size >**/
        __im_lookup_table_class_init,   /**< class_init >**/
        0,                              /**< class_finalize >**/

        sizeof (IMLookupTable),         /**< instance_size >**/
        __im_lookup_table_initialize,   /**< instance_init >**/
        __im_lookup_table_finalize,     /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_CANDIDATE,
                                      "Candidate",
                                      &im_candidate_type_info,
                                      TRUE);

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_LOOKUP_TABLE,
                                      "LookupTable",
                                      &im_lookup_table_type_info,
                                      TRUE);
}

IMCandidate*
im_candidate_new (IMText *content)
{
    IMCandidate *c;

    _im_return_val_if_fail (content == 0 || IM_IS_TEXT(content), 0);

    c = (IMCandidate *) im_object_new (IM_TYPE_CANDIDATE);

    _im_assert (c != 0);

    c->content = content;

    if (content)
        im_object_ref (content);

    return c;
}

IMCandidate*
im_candidate_new_full (IMText *content, IMText *label, IMString *icon, IMPointer annotation)
{
    IMCandidate *c;

    _im_return_val_if_fail (content == 0    || IM_IS_TEXT(content), 0);
    _im_return_val_if_fail (label == 0      || IM_IS_TEXT(label), 0);
    _im_return_val_if_fail (icon == 0       || IM_IS_STRING(icon), 0);
    _im_return_val_if_fail (annotation == 0 || IM_IS_OBJECT(annotation), 0);

    c = (IMCandidate *) im_object_new (IM_TYPE_CANDIDATE);

    _im_assert (c != 0);

    c->content     = content;
    c->label       = label;
    c->icon        = icon;
    c->annotation  = annotation;

    if (content)
        im_object_ref (content);
    if (label)
        im_object_ref (label);
    if (icon)
        im_object_ref (icon);
    if (annotation)
        im_object_ref (annotation);

    return c;
}

void
im_candidate_set_content (IMCandidate *candidate, IMText *content)
{
    _im_return_if_fail (IM_IS_CANDIDATE (candidate));
    _im_return_if_fail (content == 0 || IM_IS_TEXT(content));

    if (candidate->content)
        im_object_unref (candidate->content);
    candidate->content = content;
    if (candidate->content)
        im_object_ref (candidate->content);
}

IMText*
im_candidate_get_content (const IMCandidate *candidate)
{
    _im_return_val_if_fail (IM_IS_CANDIDATE (candidate), 0);
    return candidate->content;
}

void
im_candidate_set_label (IMCandidate *candidate, IMText *label)
{
    _im_return_if_fail (IM_IS_CANDIDATE (candidate));
    _im_return_if_fail (label == 0 || IM_IS_TEXT(label));

    if (candidate->label)
        im_object_unref (candidate->label);
    candidate->label = label;
    if (candidate->label)
        im_object_ref (candidate->label);
}

IMText*
im_candidate_get_label (const IMCandidate *candidate)
{
    _im_return_val_if_fail (IM_IS_CANDIDATE (candidate), 0);
    return candidate->label;
}

void
im_candidate_set_icon (IMCandidate *candidate, IMString *icon)
{
    _im_return_if_fail (IM_IS_CANDIDATE (candidate));
    _im_return_if_fail (icon == 0 || IM_IS_STRING(icon));

    if (candidate->icon)
        im_object_unref (candidate->icon);
    candidate->icon = icon;
    if (candidate->icon)
        im_object_ref (candidate->icon);
}

IMString*
im_candidate_get_icon (const IMCandidate *candidate)
{
    _im_return_val_if_fail (IM_IS_CANDIDATE (candidate), 0);
    return candidate->icon;
}

void
im_candidate_set_annotation (IMCandidate *candidate, IMPointer *annotation)
{
    _im_return_if_fail (IM_IS_CANDIDATE (candidate));
    _im_return_if_fail (annotation == 0 || IM_IS_OBJECT(annotation));

    if (candidate->annotation)
        im_object_unref (candidate->annotation);
    candidate->annotation = annotation;
    if (candidate->annotation)
        im_object_ref (candidate->annotation);
}

IMPointer
im_candidate_get_annotation (const IMCandidate *candidate)
{
    _im_return_val_if_fail (IM_IS_CANDIDATE (candidate), 0);
    return candidate->annotation;
}

IMLookupTable*
im_lookup_table_new (IMUInt32 id)
{
    IMLookupTable *t = (IMLookupTable *) im_object_new (IM_TYPE_LOOKUP_TABLE);

    _im_assert (t != 0);

    t->id = id;

    return t;
}

IMLookupTable*
im_lookup_table_new_full (IMUInt32 id, IMUInt orientation,
                          IMUInt cols, IMUInt rows,
                          IMLookupTableFlag *flags, IMSize n_flags)
{
    IMSize i;
    IMLookupTable *t = (IMLookupTable *) im_object_new (IM_TYPE_LOOKUP_TABLE);

    _im_assert (t != 0);

    t->id = id;
    t->orientation = orientation;

    if (cols == 0)
        cols = 1;
    else if (cols > IM_LOOKUP_TABLE_MAX_N_COLUMNS)
        cols = IM_LOOKUP_TABLE_MAX_N_COLUMNS;

    t->cols = cols;

    if (rows == 0)
        rows = 1;
    else if (rows > IM_LOOKUP_TABLE_MAX_N_ROWS)
        rows = IM_LOOKUP_TABLE_MAX_N_ROWS;

    t->rows = rows;

    if (flags && n_flags) {
        for (i = 0; i < n_flags; ++i) {
            /* FIXME: currently only 15 flags are supported. */
            _im_assert(flags[i] < 16);
            t->flags |= (1<<flags[i]);
        }
    }

    return t;
}
void
im_lookup_table_clear (IMLookupTable *table)
{
    IMSize i;

    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));

    /* Free title */
    if (table->title) {
        im_object_unref (table->title);
        table->title = 0;
    }

    /* Free all candidates */
    for (i = 0; i < im_ptr_array_size (table->candidates); ++i) {
        if (im_ptr_array_index (table->candidates, i)) {
            im_object_unref (im_ptr_array_index (table->candidates, i));
            im_ptr_array_index (table->candidates, i) = 0;
        }
    }
    im_ptr_array_clear (table->candidates);
}
void
im_lookup_table_set_id (IMLookupTable *table, IMUInt32 id)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));

    table->id = id;
}
IMUInt32
im_lookup_table_get_id (const IMLookupTable *table)
{
    _im_return_val_if_fail (IM_IS_LOOKUP_TABLE(table), (IMUInt32)-1);

    return table->id;
}
void
im_lookup_table_set_orientation (IMLookupTable *table, IMUInt orientation)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));
    table->orientation = orientation;
}
IMUInt
im_lookup_table_get_orientation (const IMLookupTable *table)
{
    _im_return_val_if_fail (IM_IS_LOOKUP_TABLE(table), IM_LOOKUP_TABLE_LTRTTB);
    return table->orientation;
}
void
im_lookup_table_set_page_size (IMLookupTable *table, IMUInt cols, IMUInt rows)
{
    IMSize new_size;
    IMSize i;

    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));

    if (cols == 0)
        cols = 1;
    else if (cols > IM_LOOKUP_TABLE_MAX_N_COLUMNS)
        cols = IM_LOOKUP_TABLE_MAX_N_COLUMNS;

    table->cols = cols;

    if (rows == 0)
        rows = 1;
    else if (rows > IM_LOOKUP_TABLE_MAX_N_ROWS)
        rows = IM_LOOKUP_TABLE_MAX_N_ROWS;

    table->rows = rows;

    new_size = cols * rows;

    /* Adjust cursor */
    if (table->cursor >= new_size)
        table->cursor = new_size - 1;

    /* Free redundant candidates */
    if (new_size < im_ptr_array_size (table->candidates)) {
        for (i = new_size; i < im_ptr_array_size (table->candidates); ++i) {
            if (im_ptr_array_index (table->candidates,i)) {
                im_object_unref (im_ptr_array_index (table->candidates,i));
                im_ptr_array_index (table->candidates,i) = 0;
            }
        }
        im_ptr_array_set_size (table->candidates, new_size);
    }
}
void
im_lookup_table_get_page_size (const IMLookupTable *table, IMUInt *cols, IMUInt *rows)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));

    if (cols)
        *cols = table->cols;
    if (rows)
        *rows = table->rows;
}
void
im_lookup_table_set_cursor (IMLookupTable *table, IMUInt cur)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));

    if (cur >= table->cols * table->rows)
        table->cursor = table->cols * table->rows - 1;
    else
        table->cursor = cur;
}
IMUInt
im_lookup_table_get_curosr (const IMLookupTable *table)
{
    _im_return_val_if_fail (IM_IS_LOOKUP_TABLE(table), 0);

    return table->cursor;
}
void
im_lookup_table_set_flag (IMLookupTable *table, IMLookupTableFlag flag,
                          IMBool state)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));
    _im_assert(flag < 16);
    if (state)
        table->flags |= (1<<flag);
    else
        table->flags &= ~(1<<flag);
}
IMBool
im_lookup_table_check_flag (const IMLookupTable *table, IMLookupTableFlag flag)
{
    _im_return_val_if_fail (IM_IS_LOOKUP_TABLE(table), FALSE);
    _im_assert(flag < 16);

    return (table->flags & (1<<flag)) != 0;
}
void
im_lookup_table_set_title (IMLookupTable *table, IMText *title)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));
    _im_return_if_fail (!title || IM_IS_TEXT (title));

    if (table->title)
        im_object_unref (table->title);

    table->title = title;

    if (title)
        im_object_ref (title);
}
IMText*
im_lookup_table_get_title (const IMLookupTable *table)
{
    _im_return_val_if_fail (IM_IS_LOOKUP_TABLE(table), 0);

    return table->title;
}
void
im_lookup_table_set_scroll_info (IMLookupTable *table, IMUInt num_pages, IMUInt cur_page)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));

    if (num_pages == 0)
        num_pages = 1;
    if (cur_page >= num_pages)
        cur_page = num_pages - 1;

    table->num_pages = num_pages;
    table->cur_page  = cur_page;
}
void
im_lookup_table_get_scroll_info (const IMLookupTable *table, IMUInt *num_pages, IMUInt *cur_page)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));

    if (num_pages)
        *num_pages = table->num_pages;
    if (cur_page)
        *cur_page = table->cur_page;
}
void
im_lookup_table_set_candidate (IMLookupTable *table, IMUInt index, IMCandidate *cand)
{
    _im_return_if_fail (IM_IS_LOOKUP_TABLE(table));
    _im_return_if_fail (cand == 0 || IM_IS_CANDIDATE(cand));
    _im_return_if_fail (index < table->cols * table->rows);

    if (im_ptr_array_size (table->candidates) <= index && cand)
        im_ptr_array_set_size (table->candidates, index + 1);

    if (im_ptr_array_size (table->candidates) > index) {
        if (im_ptr_array_index (table->candidates, index))
            im_object_unref (im_ptr_array_index (table->candidates, index));
        im_ptr_array_index (table->candidates, index) = cand;

        if (cand) im_object_ref (cand);
    }
}
IMCandidate*
im_lookup_table_get_candidate (const IMLookupTable *table, IMUInt index)
{
    _im_return_val_if_fail (IM_IS_LOOKUP_TABLE(table), 0);
    _im_return_val_if_fail (index < table->cols * table->rows, 0);

    if (index < im_ptr_array_size (table->candidates))
        return im_ptr_array_index (table->candidates, index);

    return 0;
}
/*
vi:ts=4:nowrap:ai:expandtab
*/
